package com.atguigu.gmall.index.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.index.annotation.GmallCahe;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class GmallCaCheAspect {
    @Autowired
    RedissonClient redissonClient;

    @Autowired
    RBloomFilter rBloomFilter;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Around("@annotation(com.atguigu.gmall.index.annotation.GmallCahe)")
    public Object around(ProceedingJoinPoint joinPoint)throws Throwable{
        //获取注解的参数列表
        Object[] args1 = joinPoint.getArgs();

        String args = StringUtils.join(args1, ",");
        //获取注解的前缀以及锁的名称
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取目标方法对象
        Method method = signature.getMethod();
        //获取注解对象
        Class<?> returnType = method.getReturnType();
        GmallCahe annotation = method.getAnnotation(GmallCahe.class);
        String prefix = annotation.prefix();
        String lockName = annotation.lockName();
        int random = annotation.random();
        int timeout = annotation.timeout();
        //布隆过滤器，如果不存在则直接返回null
        boolean contains = rBloomFilter.contains(prefix + args);
        if (!contains){
            return null;
        }
        //查询缓存，如果有缓存，则直接返回，没有缓存获取锁
        String json = redisTemplate.opsForValue().get(prefix + args);
        if (StringUtils.isNotBlank(json)){
            return JSON.parseObject(json,returnType);
        }
        //没有缓存，则添加分布式锁
        RLock lock = redissonClient.getLock(lockName + args);
        Random random1 = new Random();
        int i = random1.nextInt(random)+timeout;
        lock.lock();
        try {
            //再次查询，在获取锁的时间内有可能有人已经设置了缓存
            String json1 = redisTemplate.opsForValue().get(prefix + args);
            if (StringUtils.isNotBlank(json1)){
                return JSON.parseObject(json1,returnType);
            }
            //执行目标方法
            Object result = joinPoint.proceed(args1);
            //把目标方法的返回值放入缓存
            if (result!=null){
            this.redisTemplate.opsForValue().
                    set(prefix+args,JSON.toJSONString(result),i,TimeUnit.MINUTES);
            }
            return result;
        } finally {
            lock.unlock();
        }
    }


}
